home *** CD-ROM | disk | FTP | other *** search
/ MacHack 1997 / MacHack 1997.toast / Hacks / Hacks ’96 / FinderFlocks / CBoid.cp next >
Text File  |  1996-06-23  |  9KB  |  341 lines

  1. #include "CBoid.h"
  2. #include "FlockDrawing.h"
  3. #include "FinderRegistry.h"
  4.  
  5. extern long            gWindowView;
  6.  
  7. //!•
  8. GetIconBits(short id, Handle *theBits, Handle *theMaskBits);
  9.  
  10.  
  11. CBoid* CBoid::InitBoid(    Rect *worldRect, Handle theBits, Handle theMaskBits, Point position)
  12. {
  13.     short                wid, hgt, err;
  14.     Rect                rect;
  15.     
  16.     // Set other Particle variables
  17.     wid = worldRect->right - worldRect->left;
  18.     hgt = worldRect->bottom - worldRect->top;
  19.     this->fPosition.h = position.h;
  20.     this->fPosition.v = position.v;
  21.     this->fVelocity.h = 0;
  22.     this->fVelocity.v = 0;
  23.     this->fStartPoint = position;
  24.     this->fHome = false;
  25.     this->Reset(); // initializes the Neighbor record to none and sets acceleration to zero
  26.     err = this->InitBits(theBits, theMaskBits);
  27.     
  28.     if(err) SysBeep(10);
  29.     return(this);
  30. }
  31.  
  32. void CBoid::GetBoidPos(FloatPoint *pos)
  33. {
  34.     *pos = this->fPosition;
  35. }
  36.  
  37. void CBoid::GetBoidVel(FloatPoint *vel)
  38. {
  39.     *vel = this->fVelocity;
  40. }
  41.  
  42. // Re-initializes the Neighbor record and sets acceleration to zero
  43. void CBoid::Reset(void)
  44. {
  45.     // The neighbors
  46.     fNaybs.fNum = 0;
  47.     fNaybs.fAvgPos.h = 0;
  48.     fNaybs.fAvgPos.v = 0;
  49.     fNaybs.fAvgVel.h = 0;
  50.     fNaybs.fAvgVel.v = 0;
  51.     fNaybs.fAvgDistSquared = 0;
  52.     fNaybs.fNearestPos.h = 0;
  53.     fNaybs.fNearestPos.v = 0;
  54.     fNaybs.fNearestDistSquared = 2000000;     // For later "are we closer than the last?" comparisons.
  55.                                             // We want the answer to the first one to be yes.
  56.     
  57.     // The acceleration
  58.     fAcceleration.h = 0;
  59.     fAcceleration.v = 0;
  60. }
  61.  
  62. // This routine does the flock thing to get the new position,
  63. // wraps the position in the given Rect (toroidal world, don't ya know), and returns
  64. // the new position in "newPos".
  65.  
  66. void CBoid::Move(FloatPoint *newPos, Rect *flockRect, ControlRec *controls, double comfydistsq)
  67. {
  68.     double    wid, hgt;
  69.     Rect    rect;
  70.     short    rposX, rposY;
  71.     GrafPtr    currentPort;
  72.     
  73.     // If we're not home, do the usual
  74.     if(!this->fHome)
  75.     {    
  76.         // Look at neighbors and calculate new acceleration.
  77.         // This is where the flocking really happens.
  78.         NewAccel(comfydistsq, controls);
  79.  
  80.         wid = flockRect->right - flockRect->left;
  81.         hgt = flockRect->bottom - flockRect->top;
  82.         
  83.         // Keep the velocity within limits
  84.         Clamp(&this->fVelocity, controls->fMaxVelocityCtl);
  85.         
  86.         // Step the boid
  87.         this->NextStep();
  88.         
  89.         // Wrap the boid
  90.         if(this->fPosition.h < flockRect->left)
  91.             this->fPosition.h += wid;
  92.         else if(this->fPosition.h > flockRect->right)
  93.             this->fPosition.h -= wid;
  94.         // Same for v
  95.         if(this->fPosition.v < flockRect->top)
  96.             this->fPosition.v += hgt;
  97.         else if(this->fPosition.v > flockRect->bottom)
  98.             this->fPosition.v -= hgt;
  99.     }
  100.     else // we're near home, settle in
  101.     {
  102.         this->fPosition.h = this->fStartPoint.h;
  103.         this->fPosition.v = this->fStartPoint.v;
  104.     }
  105.     
  106.     // return new position
  107.     *newPos = this->fPosition;
  108.     
  109.     // convert new position to a rect
  110.     rposX = rint(this->fPosition.h);
  111.     rposY = rint(this->fPosition.v);
  112.     rect.left = rposX;
  113.     rect.top = rposY;
  114.     if(gWindowView == pIconBitmap)
  115.     {
  116.         rect.right = rposX + 32;
  117.         rect.bottom = rposY + 32;
  118.     }
  119.     else if(gWindowView == pSmallIcon)
  120.     {
  121.         rect.right = rposX + 16;
  122.         rect.bottom = rposY + 16;
  123.     }
  124.     
  125.     // Draw!!
  126.     HLock(this->fTheMap);
  127.     
  128.     // Let mask region catch up
  129.     OffsetRgn(this->fTheMaskRgn, rposX, rposY);
  130.     
  131.     // OK, slam those bits
  132.     GetPort(¤tPort);
  133.     CopyBits((BitMap *)*this->fTheMap, ¤tPort->portBits, 
  134.                 &(**(BitMapHandle)this->fTheMap).bounds, &rect, srcCopy, this->fTheMaskRgn);
  135.     HUnlock(this->fTheMap);
  136.     
  137.     // reset rgn for next time
  138.     OffsetRgn(this->fTheMaskRgn, -rposX, -rposY);
  139.     
  140.     // Inval the rect
  141.     MarkRect(&rect);
  142.  
  143.     // Reset for next time
  144.     Reset();
  145. }
  146.  
  147.  
  148. // This routine gets acceleration requests from each of the three "rules" in order,
  149. // and arbitrates the results: Each request is added into an Acceleration accumulator
  150. // (AccAcc), and it's magnitude is added into another, scalar accumulator (MagAcc).
  151. // When the accumulated magnitudes equal or exceed the maximum allowable "oompf," 
  152. // further requests are denied (actually, not even requested). The final accumulated
  153. // acceleration is trimmed back to a reasonable value and then set as the new 
  154. // acceleration.
  155. void CBoid::NewAccel(double comfydistsq, ControlRec *controls)
  156. {
  157.     long        leftover = controls->fMaxEffortCtl; 
  158.     FloatPoint     AccAcc = {0, 0}, request = {0, 0};
  159.     
  160.     // If there are no neighbors, do nothing
  161.     if(this->fNaybs.fNum == 0)
  162.         return;
  163.     
  164.     // First, avoid collisions
  165.     if(leftover > 0)
  166.     {
  167.         RunAway(comfydistsq, &request);
  168.         Clamp(&request, controls->fAvoidMaxCtl);
  169.         AccAcc = request;
  170.             
  171.         // Subtract the "energy" used by this request
  172.         leftover -= VecMagSq(request.h, request.v);
  173.         
  174.         // if something leftover, do next rule
  175.         if(leftover > 0)
  176.         {
  177.             // Match the neighbors average velocity
  178.             MatchVel(&request);
  179.             Clamp(&request, controls->fMatchMaxCtl);
  180.             AccAcc.h += request.h;
  181.             AccAcc.v += request.v;
  182.             
  183.             // Subtract the "energy" used by this request
  184.             leftover -= VecMagSq(request.h, request.v);
  185.     
  186.             // if something leftover, do next rule
  187.             if(leftover > 0)
  188.             {
  189.                 // Try to move toward the "center" of the neighbors
  190.                 CatchUp(&request);
  191.                 Clamp(&request, controls->fCenterMaxCtl);
  192.                 AccAcc.h += request.h;
  193.                 AccAcc.v += request.v;
  194.             }
  195.         }
  196.         // Trim new acceleration
  197.          Clamp(&AccAcc, controls->fMaxAccelCtl);
  198.      }
  199.     this->fAcceleration = AccAcc;
  200. }
  201.     
  202.  
  203. void CBoid :: RunAway(double comfydistsq, FloatPoint *request)
  204. {
  205.     double    scaler;
  206.     double    distsq;
  207.     double    biggest;
  208.     
  209.     // First get the distance squared to the nearest neighbor
  210.     distsq = (fNaybs.fNearestDistSquared > 0) ? fNaybs.fNearestDistSquared : 0.01; // To avoid dividing by 0
  211.     
  212.     // if distsq > comfydistsq, request no change
  213.     if(distsq > comfydistsq)
  214.     {
  215.         request->h = 0;
  216.         request->v = 0;
  217.     }
  218.     else // Check it out
  219.     {
  220.         // get the vector pointing from the nearest neighbor to us
  221.         request->h = fPosition.h - fNaybs.fNearestPos.h;
  222.         request->v = fPosition.v - fNaybs.fNearestPos.v;
  223.  
  224.         biggest = (fabs(request->v) > fabs(request->h)) ? fabs(request->v) : fabs(request->h);
  225.         if(biggest == 0)
  226.         {
  227.             request->h = Random() % 4;
  228.             request->v = Random() % 4;
  229.             biggest = (fabs(request->v) > fabs(request->h)) ? fabs(request->v) : fabs(request->h);
  230.             if(biggest == 0) biggest = 1;
  231.         }
  232.         scaler = comfydistsq / biggest;
  233.         request->h = (request->h * scaler) / distsq;
  234.         request->v = (request->v * scaler) / distsq;
  235.     }
  236. }
  237.  
  238. void CBoid :: MatchVel(FloatPoint *request)
  239. {
  240.     // Just return the difference from the neighbors' average velocity
  241.     request->h = (fNaybs.fAvgVel.h - fVelocity.h);
  242.     request->v = (fNaybs.fAvgVel.v - fVelocity.v);
  243. }
  244.  
  245. void CBoid :: CatchUp(FloatPoint *request)
  246. {
  247.     // Just return the vector pointing toward the center of the neighbors
  248.     request->h = (fNaybs.fAvgPos.h - fPosition.h);
  249.     request->v = (fNaybs.fAvgPos.v - fPosition.v);
  250. }
  251.  
  252. //----------------------------------------------
  253. // •• Icon junk
  254. //----------------------------------------------
  255. OSErr CBoid :: InitBits(Handle theBits, Handle theMaskBits)
  256. {
  257.     OSErr    err;
  258.     Rect    mapRect;
  259.     long    bitsSize, maskOffset;
  260.     Handle    theMaskBitmap;
  261.     
  262.     if(gWindowView == pIconBitmap)
  263.     {
  264.         SetRect(&mapRect, 0, 0, 32, 32);
  265.         maskOffset = 128;
  266.     }
  267.     else if(gWindowView == pSmallIcon)
  268.     {
  269.         SetRect(&mapRect, 0, 0, 16, 16);
  270.         maskOffset = 32;
  271.     }
  272.     else
  273.         return -1;
  274.         
  275.     if(theBits != nil)
  276.     {
  277.         bitsSize = BuildMap(&fTheMap, &mapRect, 8);
  278.         if(bitsSize == 0) return memFullErr;
  279.         fTheBits = NewPtr(bitsSize);
  280.         if(fTheBits == nil) return memFullErr;
  281.         
  282.         HLock(theBits);
  283.         BlockMove(*theBits, fTheBits, bitsSize);
  284.         HUnlock(theBits);
  285.     }
  286.     else // if theBits is nil, no 8 bit icon, use the black and white icon
  287.     {
  288.         bitsSize = BuildMap(&fTheMap, &mapRect, 1);
  289.         if(bitsSize == 0) return memFullErr;
  290.         fTheBits = NewPtr(bitsSize);
  291.         if(fTheBits == nil) return memFullErr;
  292.         
  293.         HLock(theMaskBits);
  294.         BlockMove(*theMaskBits, fTheBits, bitsSize);
  295.         HUnlock(theMaskBits);
  296.     }
  297.     
  298.     ((BitMap *)(*fTheMap))->baseAddr = fTheBits;
  299.     
  300.     // Set up mask Rgn
  301.     bitsSize = BuildMap(&theMaskBitmap, &mapRect, 1);
  302.     if(bitsSize == 0) return memFullErr;
  303.     
  304.     HLock(theMaskBits);
  305.     HLock(theMaskBitmap);
  306.     ((BitMap *)(*theMaskBitmap))->baseAddr = (*theMaskBits) + maskOffset;
  307.     
  308.     fTheMaskRgn = NewRgn();
  309.     BitMapToRegion(fTheMaskRgn, (BitMap *)(*theMaskBitmap));
  310.     
  311.     HUnlock(theMaskBits);
  312.     HUnlock(theMaskBitmap);
  313.     DisposeHandle(theMaskBitmap);
  314.     
  315.     return noErr;
  316. }
  317.  
  318. void CBoid :: Nudge(void)
  319. {
  320.     this->fPosition.h += Random() % 3;
  321.     this->fPosition.v += Random() % 3;
  322. }
  323.  
  324. Boolean CBoid :: HeadHome(void)
  325. {
  326.     // Still getting there
  327.     this->fNaybs.fNum = 1;
  328.     this->fNaybs.fAvgPos.h = this->fStartPoint.h;
  329.     this->fNaybs.fAvgPos.v = this->fStartPoint.v;
  330.     this->fNaybs.fAvgVel.h = 0;
  331.     this->fNaybs.fAvgVel.v = 0;
  332.     this->fNaybs.fAvgDistSquared = 
  333.                     VecMagSq(this->fPosition.h - this->fNaybs.fAvgPos.h, 
  334.                                 this->fPosition.v - this->fNaybs.fAvgPos.v );
  335.     
  336.     // if we're near home, stop
  337.     if(this->fNaybs.fAvgDistSquared <= 25)
  338.         this->fHome = true;
  339.     
  340.     return this->fHome;
  341. }